package obecto.task
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.setTimeout;
	
	import obecto.event.EventDescriptor;
	import obecto.event.TaskEvent;
	import obecto.utility.Listener;
	
	/**
	 * An <code>ExecuteAsyncMethodTask</code> object is initialized with the 
	 * <code>static</code> methods <code>create()</code> and 
	 * <code>createBasic()</code> which expect the following parameters:
	 * <ul>
	 * 	<li><code>EventDescriptor</code> object or event name, which specifies the event that should be raised in order to fire event <b><i>completed</i><b> for this <code>ExecuteAsyncMethodTask</code></li>
	 * 	<li>method scope</li>
	 * 	<li>method function (usually we expect that this method function will raise the event we are subscribed to)</li>
	 * 	<li>array of arguments for the method function</li>
	 * </ul>
	 * 
	 * @author todor
	 */
	[Event(name="start", type="obecto.event.TaskEvent")]
	[Event(name="interrupt", type="obecto.event.TaskEvent")]
	[Event(name="complete", type="obecto.event.TaskEvent")]
	public class ExecuteAsyncMethodTask extends BaseInterruptibleTask
	{	
		public var completionEventInfo : EventDescriptor;
		
		public var methodScope : *;
		public var methodFunction : Function;
		public var methodArguments : Array;
		
		public var interuptionMethodScope : *;
		public var interuptionMethodFunction : Function;
		public var interuptionMethodArguments : Array;
		
		protected static function newInstance(
			completionEventInfo : EventDescriptor,  
			methodFunction : Function, 
			methodArguments : Array, 
			interuptionMethodFunction : Function, 
			interuptionMethodArguments : Array,
			methodScope : * = null,
			interuptionMethodScope : * = null) : ExecuteAsyncMethodTask
		{
			var result : ExecuteAsyncMethodTask = new ExecuteAsyncMethodTask();
			
			result._running = false;
			result.completionEventInfo = completionEventInfo;
			
			result.methodScope = methodScope;
			result.methodFunction = methodFunction;
			result.methodArguments = methodArguments;
			
			result.interuptionMethodScope = interuptionMethodScope;
			result.interuptionMethodFunction = interuptionMethodFunction;
			result.interuptionMethodArguments = interuptionMethodArguments;
			
			return result;
		}
		
		override public function start() : void 
		{
			if (!running)
			{
				_running = true;
				
				/**
				 *	Modified in order to handle case when method dispatch the event synchronously with the method call. 
				 */
				dispatchEvent(new TaskEvent(TaskEvent.START));
				
				if (isNaN(delay) || delay == 0)
				{
					startActual();
				}
				else
				{
					delayId = setTimeout(startWithDelay, delay * 1000);
				}
			}
		}
		
		override protected function startActual() : void
		{
			dispatchEvent(new TaskEvent(TaskEvent.START));
			Listener.createSingleTimeListener(completionEventInfo.eventSource, 
				completionEventInfo.eventName, onMethodCompleted);
						
			methodFunction.apply(methodScope, methodArguments);
		} 
		
		private function onMethodCompleted(e : Event) : void
		{
			if (_running)
			{
				_running = false;
				dispatchEvent(new TaskEvent(TaskEvent.COMPLETE));
				//TODO: This was dispatchEventLater in prevoius version
			}
		}
		
		public static function create(
			completionEventInfo : EventDescriptor, 
			methodFunction : Function, 
			methodArguments : Array,
			methodScope : * = null) : ExecuteAsyncMethodTask
		{
			var result : ExecuteAsyncMethodTask = newInstance(
				completionEventInfo,
				methodFunction, 
				methodArguments,
				null,
				null,
				null,
				methodScope);
					
			return result;
		}
		
		public static function createInteruptable(
			completionEventInfo : EventDescriptor, 
			methodFunction : Function, 
			methodArguments : Array, 
			interuptionMethodFunction : Function, 
			interuptionMethodArguments : Array,
			interuptionMethodScope : * = null,
			methodScope : * = null) : ExecuteAsyncMethodTask
		{
			var result : ExecuteAsyncMethodTask = newInstance(
				completionEventInfo, 
				methodFunction, 
				methodArguments,
				interuptionMethodFunction, 
				interuptionMethodArguments,
				methodScope,
				interuptionMethodScope);
					
			return result;
		}
		
		public static function createBasic(
			completionEventName : String, 
			methodFunction : Function, 
			methodArguments : Array,
			methodScope : IEventDispatcher = null) : ExecuteAsyncMethodTask
		{
			var completionEventInfo : EventDescriptor = EventDescriptor.create(methodScope, completionEventName);
				
			var result : ExecuteAsyncMethodTask = newInstance(
				completionEventInfo,
				methodFunction, 
				methodArguments,
				null,
				null,
				null,
				methodScope);
					
			return result;
		}
		
		public static function createBasicInteruptable(
			completionEventName : String,  
			methodFunction : Function, 
			methodArguments : Array, 
			interuptionMethodFunction : Function, 
			interuptionMethodArguments : Array,
			methodScope : * = null,
			interuptionMethodScope : * = null) : ExecuteAsyncMethodTask
		{
			var completionEventInfo:EventDescriptor = EventDescriptor.create(methodScope, completionEventName);
				
			var result : ExecuteAsyncMethodTask = newInstance(
				completionEventInfo,  
				methodFunction, 
				methodArguments, 
				interuptionMethodFunction, 
				interuptionMethodArguments,
				methodScope,
				interuptionMethodScope);
					
			return result;
		}
				
	}
}